#include "graphics/renderer.h"


namespace Jin {

	void BufferPainter::DirectGetColor(int x, int y, Color32& color) const noexcept {
		color = m_pixelBuffer->GetPixel(x ,y);
	}

	void BufferPainter::DirectDrawPoint(int x, int y) const noexcept {
		_DirectDrawPointColor(x, y, style.drawColor);
	}

	void BufferPainter::DirectDrawPointColor(int x, int y, Color32 color) const noexcept {
		_DirectDrawPointColor(x, y, color);
	}

	void BufferPainter::DirectDrawPointStyle(int x, int y, const DrawStyle& style) const noexcept {
		_DirectDrawPointStyle(x, y, style);
	}

	void BufferPainter::DirectDrawLine(int x1, int y1, int x2, int y2) const noexcept {
		_DirectDrawLineColor(x1, y1, x2, y2, style.drawColor);
	}

	void BufferPainter::DirectDrawLineColor(int x1, int y1, int x2, int y2, Color32 color) const noexcept {
		_DirectDrawLineColor(x1, y1, x2, y2, color);
	}

	void BufferPainter::DirectDrawLineStyle(int x1, int y1, int x2, int y2, const DrawStyle& style) const noexcept {
		_DirectDrawLineStyle(x1, y1, x2, y2, style);
	}

	void BufferPainter::DirectFillRect(const Rect2D& rect) const noexcept {
		_DirectFillRectColor(rect, style.drawColor);
	}

	void BufferPainter::DirectFillRectColor(const Rect2D& rect, Color32 color) const noexcept {
		_DirectFillRectColor(rect, color);
	}

	void BufferPainter::DirectFillRectStyle(const Rect2D& rect, const DrawStyle& style) const noexcept {
		_DirectFillRectStyle(rect, style);
	}

	void BufferPainter::DirectInvertColor(const Rect2D& rect) const noexcept {
		m_pixelBuffer->InvertColor(rect.pos.x, rect.pos.y, rect.size.w, rect.size.h);
	}

	void BufferPainter::DirectInvertColorMono(const Rect2D& rect) const noexcept {
		m_pixelBuffer->InvertColorMono(rect.pos.x, rect.pos.y, rect.size.w, rect.size.h);
	}

	void BufferPainter::DirectGrayColor(const Rect2D& rect) const noexcept {
		m_pixelBuffer->GrayColor(rect.pos.x, rect.pos.y, rect.size.w, rect.size.h);
	}

	void BufferPainter::DirectDrawFTBitmap(int dstX, int dstY, const FT_Bitmap& src, const Rect2D& srcRect) const noexcept {
		_DirectDrawFTBitmapColor(dstX, dstY, src, srcRect, style.drawColor);
	}

	void BufferPainter::DirectDrawFTBitmapColor(int dstX, int dstY, const FT_Bitmap& src, const Rect2D& srcRect, Color32 color) const noexcept {
		_DirectDrawFTBitmapColor(dstX, dstY, src, srcRect, color);
	}

	void BufferPainter::DirectDrawFTBitmapStyle(int dstX, int dstY, const FT_Bitmap& src, const Rect2D& srcRect, const DrawStyle& style) const noexcept {
		_DirectDrawFTBitmapStyle(dstX, dstY, src, srcRect, style);
	}

	void BufferPainter::DirectClear() const noexcept {
		m_pixelBuffer->FillRect(0, 0, canvasRect.size.w, canvasRect.size.h, {});
	}

	void SDLSurfacePainter::SetDrawBlendMode(BlendMode mode) const noexcept {
		m_surface.SetDrawBlendMode(mode);
	}

	void SDLSurfacePainter::SetDrawColor(Color32 color) const noexcept {
		m_surface.SetDrawColor(color);
	}

	void SDLSurfacePainter::SetDrawStyle(const DrawStyle& style) const noexcept {
		m_surface.SetDrawColor(style.drawColor);
		m_surface.SetDrawBlendMode(style.blendMode);
	}

	SDLSurface::SDLSurface(SDL_Surface* surface, bool destroy) noexcept : m_painter(*this), m_sdlSurface(surface), m_destroy(destroy) {
		if (m_sdlSurface) {
			m_painter.m_pixelBuffer = CreatePixelBuffer(m_sdlSurface->format);
		}
	}

	SDLSurface::SDLSurface(int width, int height, PixelFormat format) noexcept : m_painter(*this) {
		m_sdlSurface = SDL_CreateSurface(width, height, format);
		if (m_sdlSurface) {
			m_painter.m_pixelBuffer = CreatePixelBuffer(format);
		}
	}

	SDLSurface::~SDLSurface() noexcept {
		if (m_destroy) {
			SDL_DestroySurface(m_sdlSurface);
		}
		m_sdlSurface = nullptr;
		m_painter.m_pixelBuffer.reset();
	}

	bool SDLSurface::ConvertToFormat(PixelFormat format) noexcept {
		if (m_painter.locked) {
			return false;
		}
		if (m_sdlSurface && (m_sdlSurface->format != format)) {
			SDL_Surface* newSurface = SDL_ConvertSurface(m_sdlSurface, format);
			if (!newSurface) {
				return false;
			}
			if (m_destroy) {
				SDL_DestroySurface(m_sdlSurface);
			}
			m_sdlSurface = newSurface;
			m_painter.m_pixelBuffer = CreatePixelBuffer(format);
		}
		return true;
	}

	const Painter& SDLSurface::BeginPaint(const Rect2D* rect) noexcept {
		if (m_painter.m_pixelBuffer) {
			if (m_painter.locked == 0) {
				GetClipRect(m_painter.canvasRect);
				if (rect) {
					m_painter.canvasRect.IntersectRect(*rect);
				}
				if (m_painter.canvasRect.IsEmpty() || !Lock(m_painter.m_pixelBuffer->buffer, m_painter.m_pixelBuffer->pitch)) {
					m_painter.canvasRect = {};
					return m_painter;
				}
				m_painter.m_pixelBuffer->SeekBuffer(m_painter.canvasRect.pos);
			}
			++m_painter.locked;
		}
		return m_painter;
	}

	void SDLSurface::EndPaint() noexcept{
		if (m_painter.locked > 0) {
			--m_painter.locked;
			if (m_painter.locked == 0) {
				m_painter.canvasRect = {};
				Unlock();
			}
		}
	}

	void SDLTexturePainter::SetDrawBlendMode(BlendMode mode) const noexcept {
		m_texture.SetDrawBlendMode(mode);
	}

	void SDLTexturePainter::SetDrawColor(Color32 color) const noexcept {
		m_texture.SetDrawColor(color);
	}

	void SDLTexturePainter::SetDrawStyle(const DrawStyle& style) const noexcept {
		m_texture.SetDrawColor(style.drawColor);
		m_texture.SetDrawBlendMode(style.blendMode);
	}

	SDLTexture::SDLTexture(SDL_Texture* texture, bool destroy) noexcept : m_painter(*this), m_sdlTexture(texture), m_destroy(destroy) {
		if (m_sdlTexture && (GetTextureAccess() == SDL_TEXTUREACCESS_STREAMING)) {
			m_painter.m_pixelBuffer = CreatePixelBuffer(m_sdlTexture->format);
		}
	}

	SDLTexture::SDLTexture(const SDLRenderer& renderer, PixelFormat format, TextureAccess access, int w, int h) noexcept : m_painter(*this) {
		m_sdlTexture = renderer.CreateSDLTexture(format, access, w, h);
		if (m_sdlTexture && (access == SDL_TEXTUREACCESS_STREAMING)) {
			m_painter.m_pixelBuffer = CreatePixelBuffer(format);
		}
	}

	SDLTexture::~SDLTexture() noexcept {
		if (m_sdlTexture) {
			if (m_painter) {
				m_painter.locked = 0;
				Unlock();
			}
			if (m_destroy) {
				SDL_DestroyTexture(m_sdlTexture);
			}
			m_sdlTexture = nullptr;
		}
	}

	const Painter& SDLTexture::BeginPaint(const Rect2D* rect) noexcept {
		if (m_painter.m_pixelBuffer) {
			if (m_painter.locked == 0) {
				m_painter.canvasRect = Size2D{ Width(), Height() };
				if (rect) {
					m_painter.canvasRect.IntersectRect(*rect);
				}
				if (m_painter.canvasRect.IsEmpty() || !Lock(m_painter.canvasRect, m_painter.m_pixelBuffer->buffer, m_painter.m_pixelBuffer->pitch)) {
					m_painter.canvasRect = {};
					return m_painter;
				}
			}
			++m_painter.locked;
		}
		return m_painter;
	}

	void SDLTexture::EndPaint() noexcept {
		if (m_painter.locked > 0) {
			--m_painter.locked;
			if (m_painter.locked == 0) {
				m_painter.canvasRect = {};
				Unlock();
			}
		}
	}

	void SDLRendererPainter::SetDrawBlendMode(BlendMode blendmode) const noexcept {
		m_renderer.SetDrawBlendMode(blendmode);
	}

	void SDLRendererPainter::SetDrawColor(Color32 color) const noexcept {
		m_renderer.SetDrawColor(color);
	}

	void SDLRendererPainter::SetDrawStyle(const DrawStyle& style) const noexcept {
		m_renderer.SetDrawColor(style.drawColor);
		m_renderer.SetDrawBlendMode(style.blendMode);
	}

	void SDLRendererPainter::DirectGetColor(int x, int y, Color32& color) const noexcept {
		Rect2D direct{ Point2D{ x, y }, Size2D{ 1, 1} };
		SDLSurface surface{ m_renderer.ReadPixels(direct) };
		if (surface) {
			Graphics graphics{ surface, nullptr };
			if (graphics) {
				graphics->DirectGetColor(0, 0, color);
			}
		}
	}

	void SDLRendererPainter::DirectDrawPoint(int x, int y) const noexcept {
		m_renderer.DrawPoint(float(x), float(y));
	}

	void SDLRendererPainter::DirectDrawPointColor(int x, int y, Color32 color) const noexcept {
		auto bakDrawColor = style.drawColor;
		m_renderer.SetDrawColor(color);
		m_renderer.DrawPoint(float(x), float(y));
		m_renderer.SetDrawColor(bakDrawColor);
	}

	void SDLRendererPainter::DirectDrawPointStyle(int x, int y, const DrawStyle& style) const noexcept {
		auto bakDrawColor = this->style.drawColor;
		auto bakBlendMode = this->style.blendMode;
		m_renderer.SetDrawColor(style.drawColor);
		m_renderer.SetDrawBlendMode(style.blendMode);
		m_renderer.DrawPoint(float(x), float(y));
		m_renderer.SetDrawBlendMode(bakBlendMode);
		m_renderer.SetDrawColor(bakDrawColor);
	}

	void SDLRendererPainter::DirectDrawLine(int x1, int y1, int x2, int y2) const noexcept {
		m_renderer.DrawLine(float(x1), float(y1), float(x2), float(y2));
	}

	void SDLRendererPainter::DirectDrawLineColor(int x1, int y1, int x2, int y2, Color32 color) const noexcept {
		auto bakDrawColor = style.drawColor;
		m_renderer.SetDrawColor(color);
		m_renderer.DrawLine(float(x1), float(y1), float(x2), float(y2));
		m_renderer.SetDrawColor(bakDrawColor);
	}

	void SDLRendererPainter::DirectDrawLineStyle(int x1, int y1, int x2, int y2, const DrawStyle& style) const noexcept {
		auto bakDrawColor = this->style.drawColor;
		auto bakBlendMode = this->style.blendMode;
		m_renderer.SetDrawColor(style.drawColor);
		m_renderer.SetDrawBlendMode(style.blendMode);
		m_renderer.DrawLine(float(x1), float(y1), float(x2), float(y2));
		m_renderer.SetDrawBlendMode(bakBlendMode);
		m_renderer.SetDrawColor(bakDrawColor);
	}

	void SDLRendererPainter::DirectFillRect(const Rect2D& rect) const noexcept {
		Rect2DF direct{ rect };
		m_renderer.FillRect(&direct);
	}

	void SDLRendererPainter::DirectFillRectColor(const Rect2D& rect, Color32 color) const noexcept {
		Rect2DF direct{ rect };
		auto bakDrawColor = style.drawColor;
		m_renderer.SetDrawColor(color);
		m_renderer.FillRect(&direct);
		m_renderer.SetDrawColor(bakDrawColor);
	}

	void SDLRendererPainter::DirectFillRectStyle(const Rect2D& rect, const DrawStyle& style) const noexcept {
		Rect2DF direct{ rect };
		auto bakDrawColor = this->style.drawColor;
		auto bakBlendMode = this->style.blendMode;
		m_renderer.SetDrawColor(style.drawColor);
		m_renderer.SetDrawBlendMode(style.blendMode);
		m_renderer.FillRect(&direct);
		m_renderer.SetDrawBlendMode(bakBlendMode);
		m_renderer.SetDrawColor(bakDrawColor);
	}

	void SDLRendererPainter::DirectInvertColor(const Rect2D& rect) const noexcept {
		Rect2D direct{ rect };
		SDLSurface surface{ m_renderer.ReadPixels(direct) };
		if (surface) {
			Graphics graphics{ surface, nullptr };
			if (graphics) {
				graphics->DirectInvertColor(graphics->canvasRect);
				auto bakDrawColor = style.drawColor;
				int y = direct.pos.y;
				for (int h = 0; h < direct.size.h; ++h) {
					int x = direct.pos.x;
					for (int w = 0; w < direct.size.w; ++w) {
						Color32 color;
						graphics->DirectGetColor(x - direct.pos.x, y - direct.pos.y, color);
						m_renderer.SetDrawColor(color);
						m_renderer.DrawPoint((float)x, (float)y);
						++x;
					}
					++y;
				}
				m_renderer.SetDrawColor(bakDrawColor);
			}
		}
	}

	void SDLRendererPainter::DirectInvertColorMono(const Rect2D& rect) const noexcept {
		Rect2D direct{ rect };
		SDLSurface surface{ m_renderer.ReadPixels(direct) };
		if (surface) {
			Graphics graphics{ surface, nullptr };
			if (graphics) {
				graphics->DirectInvertColorMono(graphics->canvasRect);
				auto bakDrawColor = style.drawColor;
				int y = direct.pos.y;
				for (int h = 0; h < direct.size.h; ++h) {
					int x = direct.pos.x;
					for (int w = 0; w < direct.size.w; ++w) {
						Color32 color;
						graphics->DirectGetColor(x - direct.pos.x, y - direct.pos.y, color);
						m_renderer.SetDrawColor(color);
						m_renderer.DrawPoint((float)x, (float)y);
						++x;
					}
					++y;
				}
				m_renderer.SetDrawColor(bakDrawColor);
			}
		}
	}

	void SDLRendererPainter::DirectGrayColor(const Rect2D& rect) const noexcept {
		Rect2D direct{ rect };
		SDLSurface surface{ m_renderer.ReadPixels(direct) };
		if (surface) {
			Graphics graphics{ surface, nullptr };
			if (graphics) {
				graphics->DirectGrayColor(graphics->canvasRect);
				auto bakDrawColor = style.drawColor;
				int y = direct.pos.y;
				for (int h = 0; h < direct.size.h; ++h) {
					int x = direct.pos.x;
					for (int w = 0; w < direct.size.w; ++w) {
						Color32 color;
						graphics->DirectGetColor(x - direct.pos.x, y - direct.pos.y, color);
						m_renderer.SetDrawColor(color);
						m_renderer.DrawPoint((float)x, (float)y);
						++x;
					}
					++y;
				}
				m_renderer.SetDrawColor(bakDrawColor);
			}
		}
	}

	void SDLRendererPainter::DirectDrawFTBitmap(int dstX, int dstY, const FT_Bitmap& src, const Rect2D& srcRect) const noexcept {
		auto startX = dstX;
		auto startBuff = src.buffer + srcRect.pos.x + srcRect.pos.y * src.pitch;
		Color32 bakDrawColor = style.drawColor;
		Color32 color = style.drawColor;
		for (int h = 0; h < srcRect.size.h; ++h) {
			auto srcBuff = startBuff;
			for (int w = 0; w < srcRect.size.w; ++w) {
				auto srcAlpha = *srcBuff++;
				srcAlpha = (srcAlpha * bakDrawColor.a) >> 8;
				if (srcAlpha) {
					color.a = srcAlpha;
					m_renderer.SetDrawColor(color);
					m_renderer.DrawPoint((float)dstX, (float)dstY);
				}
				++dstX;
			}
			startBuff += src.pitch;
			dstX = startX;
			++dstY;
		}
		m_renderer.SetDrawColor(bakDrawColor);
	}

	void SDLRendererPainter::DirectDrawFTBitmapColor(int dstX, int dstY, const FT_Bitmap& src, const Rect2D& srcRect, Color32 color) const noexcept {
		auto bakDrawColor = style.drawColor;
		m_renderer.SetDrawColor(color);
		DirectDrawFTBitmap(dstX, dstY, src, srcRect);
		m_renderer.SetDrawColor(bakDrawColor);
	}

	void SDLRendererPainter::DirectDrawFTBitmapStyle(int dstX, int dstY, const FT_Bitmap& src, const Rect2D& srcRect, const DrawStyle& style) const noexcept {
		auto bakDrawColor = this->style.drawColor;
		auto bakBlendMode = this->style.blendMode;
		m_renderer.SetDrawColor(style.drawColor);
		m_renderer.SetDrawBlendMode(style.blendMode);
		DirectDrawFTBitmap(dstX, dstY, src, srcRect);
		m_renderer.SetDrawBlendMode(bakBlendMode);
		m_renderer.SetDrawColor(bakDrawColor);
	}

	void SDLRendererPainter::DirectClear() const noexcept {
		auto bakDrawColor = style.drawColor;
		auto bakBlendMode = style.blendMode;
		m_renderer.SetDrawBlendMode(SDL_BLENDMODE_NONE);
		m_renderer.SetDrawColor({});
		m_renderer.FillRect(nullptr);
		m_renderer.SetDrawColor(bakDrawColor);
		m_renderer.SetDrawBlendMode(bakBlendMode);
	}

	SDLRenderer::SDLRenderer(const SDLWindow& window, stringv dirver) noexcept : m_painter(*this) {
		m_sdlRenderer = SDL_CreateRenderer(window, dirver.data());
	}

	SDLRenderer::~SDLRenderer() noexcept {
		SDL_DestroyRenderer(m_sdlRenderer);
		m_sdlRenderer = nullptr;
		m_targetTexture = nullptr;
	}

	const Painter& SDLRenderer::BeginPaint(const Rect2D* rect) noexcept {
		if (m_painter.locked == 0) {
			if (IsClipEnabled()) {
				GetClipRect(m_painter.canvasRect);
			}
			if (HasViewport()) {
				GetViewport(m_backupViewport);
				if (!m_painter.canvasRect.IsEmpty()) {
					if (!m_painter.canvasRect.IntersectRect(m_backupViewport)) {
						return m_painter;
					}
				}
				else {
					m_painter.canvasRect = m_backupViewport;
				}
			}
			if (m_painter.canvasRect.IsEmpty()) {
				GetOutputSize(m_painter.canvasRect.size.w, m_painter.canvasRect.size.h);
			}
			if (rect) {
				m_painter.canvasRect.IntersectRect(*rect);
			}
			if (m_painter.canvasRect.IsEmpty() || !SetViewport(m_painter.canvasRect)) {
				m_painter.canvasRect = {};
				return m_painter;
			}
		}
		++m_painter.locked;
		return m_painter;
	}

	void SDLRenderer::EndPaint() noexcept {
		if (m_painter.locked > 0) {
			--m_painter.locked;
			if (m_painter.locked == 0) {
				m_painter.canvasRect = {};
				SetViewport(m_backupViewport.IsEmpty() ? nullptr : &m_backupViewport);
			}
		}
	}
}